/**
 *  Copyright 2002-2009 the original author or authors.
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package foo.bar.wiki.domain;

import foo.bar.wiki.security.PermissionType;
import foo.bar.wiki.security.RoleType;

import javax.persistence.*;

import java.util.*;
import java.io.Serializable;


/**
 * Represents a User.
 *
 * @author tmjee
 * @version $Date$ $Id$
 */
@Entity
@Table(name="TBL_USER")
@EntityListeners({EntityAuditListener.class})
@NamedQueries({
    @NamedQuery(name = "User_findByUsername",
                query = "select user from User as user where user.username = :username"),
    @NamedQuery(name ="User_findByUserId",
                query = "select user from User as user where user.id = :userId"),
    @NamedQuery(name="User_findAllUsers",
                query="select user from User as user"),
    @NamedQuery(name="User_countAllUsers",
                query="select count(user) from User as user"),
    @NamedQuery(name="User_findByUsernameAndEmail",
                query="select user from User as user where user.username = :username and user.email = :email"),
    @NamedQuery(name="User_findFavouritePages",
                query="select favPages from User as user inner join user.favouritePages as favPages where user = :user"),
    @NamedQuery(name="User_countFavouritePages",
                query="select count(favPages) from User as user inner join user.favouritePages as favPages where user = :user")
})
public class User implements Identifiable, Serializable {


    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private String id;


    @OneToMany(targetEntity = Property.class, cascade = CascadeType.ALL, mappedBy = "owner")
    private List<Property> properties = new ArrayList<Property>();


    @OneToMany(targetEntity = Role.class, cascade=CascadeType.ALL, mappedBy = "owner")
    private List<Role> roles = new ArrayList<Role>();


    @OneToMany(targetEntity = Permission.class, cascade=CascadeType.ALL, mappedBy = "owner")
    private List<Permission> permissions = new ArrayList<Permission>();


    @Basic
    @Column(name="USERNAME")
    private String username;

    @Basic
    @Column(name="EMAIL")
    private String email;

    @Basic
    @Column(name="PASSWORD")
    private String password;

    @Basic
    @Column(name="LOCKED")
    private boolean locked;

    @Basic
    @Column(name="DISABLED")
    private boolean disabled;

    @Basic
    @Column(name="EXPIRY_DATE")
    private Date expiryDate;

    @ManyToMany(targetEntity = Page.class)
    @JoinTable(
        name="TBL_USER_FAVOURITE_PAGE",
        joinColumns = {
            @JoinColumn(name="USER_ID", referencedColumnName = "ID")
        },
        inverseJoinColumns = {
            @JoinColumn(name="PAGE_ID", referencedColumnName = "ID")        
        })
    private List<Page> favouritePages = new ArrayList<Page>();




    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public List<Property> getProperties() {
        if (properties == null) {
            setProperties(new ArrayList<Property>());
        }
        return properties;
    }

    public void setProperties(List<Property> properties) {
        this.properties = properties;
    }

    public List<Role> getRoles() {
        if (roles == null) {
            setRoles(new ArrayList<Role>());
        }
        return roles;
    }

    public void setRoles(List<Role> roles) {
        this.roles = roles;
    }

    public List<Permission> getPermissions() {
        if (permissions == null) {
            setPermissions(new ArrayList<Permission>());
        }
        return permissions;
    }

    public void setPermissions(List<Permission> permissions) {
        this.permissions = permissions;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
       this.password = password;
    }

    


    public boolean isLocked() {
        return locked;
    }

    public void setLocked(boolean locked) {
        this.locked = locked;
    }

    public boolean isDisabled() {
        return disabled;
    }

    public void setDisabled(boolean disabled) {
        this.disabled = disabled;
    }

    public Date getExpiryDate() {
        return expiryDate;
    }

    public void setExpiryDate(Date expiryDate) {
        this.expiryDate = expiryDate;
    }

    public boolean isCredentialExpired() {
        return (((expiryDate != null) && (new Date().compareTo(expiryDate)) >= 0));
    }

    public List<Page> getFavouritePages() {
        return favouritePages;
    }

    public void setFavouritePages(List<Page> favouritePages) {
        this.favouritePages = favouritePages;
    }

    //////////////////////////////////////////////////////////////////////////////////

    public Set<RoleType> getRoleTypes() {
        Set<RoleType> result = new LinkedHashSet<RoleType>();
        for (Role role : getRoles()) {
            result.add(role.getType());
        }
        return result;
    }

    public Set<Role> updateRole(Set<RoleType> roleTypes) {
        if (roleTypes != null) {
            for (RoleType roleType: roleTypes) { // add the roles
                if (!hasRole(roleType)) {
                    Role r = new Role();
                    r.setType(roleType);
                    r.setOwner(this);
                    getRoles().add(r);
                }
            }
            Set<Role> rolesToBeRemoved = new LinkedHashSet<Role>(); // remove the roles
            for (Role role : getRoles()) {
                if (!roleTypes.contains(role.getType())) {
                    role.setOwner(null);
                    rolesToBeRemoved.add(role);
                }
            }
            getRoles().removeAll(rolesToBeRemoved);
            return rolesToBeRemoved;
        }
        else {  // remove all roles.
            Set<Role> rolesToBeRemoved = new LinkedHashSet<Role>();
            for (Role role : getRoles()) {
                role.setOwner(null);
                rolesToBeRemoved.add(role);
            }
            getRoles().removeAll(rolesToBeRemoved);
            return rolesToBeRemoved;
        }
    }

    public boolean hasRole(RoleType roleType) {
        for (Role role : getRoles()) {
            if (role.getType().equals(roleType)) {
                return true;
            }
        }
        return false;
    }

    public void addRole(RoleType roleType) {
        boolean contains = false;
        for (Iterator i = getRoles().iterator(); i.hasNext(); ) {
            Role role = (Role) i.next();
            if (role.getType().equals(roleType)) {
                contains = true;
            }
        }
        if (! contains) {
            Role r = new Role();
            r.setType(roleType);
            getRoles().add(r);
        }
    }
    public void removeRole(RoleType roleType) {
        for (Iterator i = getRoles().iterator(); i.hasNext(); ) {
            Role role = (Role)i.next();
            if (role.getType().equals(roleType)) {
                i.remove();
            }
        }
    }
    public Role getRole(RoleType roleType) {
        for (Role role : getRoles()) {
            if (role.getType().equals(roleType)) {
                return role;
            }
        }
        return null;
    }


    public boolean hasPermission(PermissionType permissionType) {
        for(Permission permission : permissions) {
            if (permission.getType().equals(permissionType))  {
                return true;
            }
        }
        return false;
    }
    public void removePermission(PermissionType permissionType) {
        for (Iterator i = permissions.iterator(); i.hasNext();) {
            Permission permission = (Permission) i.next();
            if (permission.getType().equals(permissionType)) {
                i.remove();
            }
        }
    }
    public Permission getPermission(PermissionType permissionType) {
        for(Permission permission : permissions) {
            if (permission.getType().equals(permissionType))  {
                return permission;
            }
        }
        return null;
    }



    public Property getProperty(String propertyName) {
        if (propertyName != null && (propertyName.trim().length() > 0)) {
            for (Property property : properties) {
                if (propertyName.equals(property.getName().trim())) {
                    return property;
                }
            }
        }
        return null;
    }


    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        User user = (User) o;

        if (username != null ? !username.equals(user.username) : user.username != null) return false;

        return true;
    }

    public int hashCode() {
        int result;
        result = (id != null ? id.hashCode() : 0);
        result = 31 * result + (properties != null ? properties.hashCode() : 0);
        result = 31 * result + (roles != null ? roles.hashCode() : 0);
        result = 31 * result + (permissions != null ? permissions.hashCode() : 0);
        result = 31 * result + (username != null ? username.hashCode() : 0);
        result = 31 * result + (password != null ? password.hashCode() : 0);
        result = 31 * result + (locked ? 1 : 0);
        result = 31 * result + (disabled ? 1 : 0);
        result = 31 * result + (expiryDate != null ? expiryDate.hashCode() : 0);
        return result;
    }
}
